package com.hxl.tech.gateway.auth.oauth;


import com.hxl.tech.gateway.common.constant.AppConstant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 自定义Jwt Token存储
 * @author soliddq
 * @date 2023-10-14
 */
public class MyJwtTokenStore extends JwtTokenStore {

    private MyJwtAccessTokenConverter jwtTokenEnhancer;
    private ApprovalStore approvalStore;

    public MyJwtTokenStore(MyJwtAccessTokenConverter jwtTokenEnhancer) {
        super(jwtTokenEnhancer);
        this.jwtTokenEnhancer = jwtTokenEnhancer;
    }

    @Override
    public void setApprovalStore(ApprovalStore approvalStore) {
        super.setApprovalStore(approvalStore);
        this.approvalStore = approvalStore;
    }

    /**
     * 生成授权信息
     * @param refreshToken
     * @param authentication
     */
    @Override
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        if(authentication.getUserAuthentication() != null) {
            OAuthUser oAuthUser = (OAuthUser) authentication.getUserAuthentication().getPrincipal();
            if(oAuthUser.getMerchantCode() != null) {
                String clientId = authentication.getOAuth2Request().getClientId();
                Calendar instance = Calendar.getInstance();
                instance.set(2200, Calendar.JANUARY,1, 0, 0, 0);
                Approval approval = new Approval(oAuthUser.getMerchantCode(), clientId, authentication.getOAuth2Request().getScope().stream().collect(Collectors.joining(",")), instance.getTime(), Approval.ApprovalStatus.APPROVED);
                approvalStore.addApprovals(Collections.singleton(approval));
            }
        }
    }

    /**
     * 刷新授权信息
     * @param tokenValue
     * @return
     */
    @Override
    public OAuth2RefreshToken readRefreshToken(String tokenValue) {
        OAuth2AccessToken encodedRefreshToken = convertAccessToken(tokenValue);
        OAuth2RefreshToken refreshToken = createRefreshToken(encodedRefreshToken);
        String type = (String)encodedRefreshToken.getAdditionalInformation().get(AppConstant.HEADER_OPEN_MERCHANT_TYPE);
        String merchantCode = (String)encodedRefreshToken.getAdditionalInformation().get(AppConstant.HEADER_OPEN_MERCHANT_CODE);
        if (Objects.nonNull(approvalStore) && StringUtils.isNotBlank(type) && StringUtils.isNotBlank(merchantCode)) {
            OAuth2Authentication authentication = readAuthentication(tokenValue);
            if (authentication.getUserAuthentication() != null) {
                String appId = authentication.getOAuth2Request().getClientId();
                Collection<Approval> approvals = approvalStore.getApprovals(merchantCode, appId);
                Collection<String> approvedScopes = new HashSet<>();
                for (Approval approval : approvals) {
                    if (approval.isApproved()) {
                        approvedScopes.add(approval.getScope());
                    }
                }
                if (!approvedScopes.containsAll(authentication.getOAuth2Request().getScope())) {
                    return null;
                }
            }
        }
        return refreshToken;
    }

    private OAuth2AccessToken convertAccessToken(String tokenValue) {
        return jwtTokenEnhancer.extractAccessToken(tokenValue, jwtTokenEnhancer.decode(tokenValue));
    }

    private OAuth2RefreshToken createRefreshToken(OAuth2AccessToken encodedRefreshToken) {
        if (!jwtTokenEnhancer.isRefreshToken(encodedRefreshToken)) {
            throw new InvalidTokenException("Encoded token is not a refresh token");
        }
        if (encodedRefreshToken.getExpiration()!=null) {
            return new DefaultExpiringOAuth2RefreshToken(encodedRefreshToken.getValue(),
                    encodedRefreshToken.getExpiration());
        }
        return new DefaultOAuth2RefreshToken(encodedRefreshToken.getValue());
    }

}
